package com.tianhe.common.tongsuo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

@Component
@Slf4j
public class SM4JNI {
//    static {
//        System.loadLibrary("libgmjni"); // ���� libgmjni.so �� gmjni.dll
//    }

    static {
        // 先加载依赖库
        //获取操作系统类型
        String osName = System.getProperties().getProperty("os.name").toLowerCase();
        if (osName != null && osName.startsWith("linux")) {
            System.load("/home/gm/lib/libgmjni.so");
        } else if (osName != null && osName.startsWith("windows")) {
            String loadLibrary = System.getProperty("user.dir")  + File.separator+ "sm" + File.separator+"gm"+File.separator+"lib"+File.separator;
            String library = "libgmjni.dll";
            System.out.println(loadLibrary+library);
            System.load(System.getProperty("user.dir") + File.separator+"sm"+ File.separator+"tongsuo"+ File.separator+"bin"+ File.separator+"libcrypto-3-x64.dll");
            System.load(System.getProperty("user.dir") + File.separator+"sm"+ File.separator+"tongsuo"+ File.separator+"bin"+ File.separator+"libssl-3-x64.dll");
            System.load(System.getProperty("user.dir") + File.separator+"sm"+ File.separator+"gm"+ File.separator+"lib"+ File.separator+"libgmjni.dll");
        } else {
            log.info("其他操作系统");
        }
    }

    public native int cbcEncrypt(byte[] key, byte[] iv, byte[] plain, byte[] cipher);
    public native int cbcDecrypt(byte[] key, byte[] iv, byte[] cipher, byte[] plain);
    public native int ecbEncrypt(byte[] key, byte[] plain, byte[] cipher);
    public native int ecbDecrypt(byte[] key, byte[] cipher, byte[] plain);

    /**
     * SM4-CBC加密
     * @param ivHex 16字节初始化向量的十六进制字符串
     * @param plaintext 明文字符串
     * @return 加密后的十六进制字符串
     */
    public String cbcEncrypt(String ivHex, String plaintext) {
        SM4JNI sm4 = new SM4JNI();
        byte[] key = new byte[16];
        byte[] iv = ivHex.getBytes();
        byte[] plainBytes = plaintext.getBytes(StandardCharsets.UTF_8);

        byte[] ciphertext = new byte[plainBytes.length + 32]; // 预留padding空间
        int cipherLen = sm4.cbcEncrypt(key, iv, plainBytes, ciphertext);

        if (cipherLen <= 0) {
            throw new RuntimeException("SM4 CBC加密失败");
        }

        return bytesToHex(Arrays.copyOf(ciphertext, cipherLen));
    }

    /**
     * SM4-CBC解密
     * @param ivHex 16字节初始化向量的十六进制字符串
     * @param ciphertextHex 密文的十六进制字符串
     * @return 解密后的原始字符串
     */
    public String cbcDecrypt(String ivHex, String ciphertextHex) {
        SM4JNI sm4 = new SM4JNI();
        byte[] key = new byte[16];
        byte[] iv = ivHex.getBytes();
        byte[] ciphertext = hexToBytes(ciphertextHex);

        byte[] plaintext = new byte[ciphertext.length]; // 解密后数据不会比密文长
        int plainLen = sm4.cbcDecrypt(key, iv, ciphertext, plaintext);

        if (plainLen <= 0) {
            throw new RuntimeException("SM4 CBC解密失败");
        }

        return new String(plaintext, 0, plainLen, StandardCharsets.UTF_8);
    }

    /**
     * SM4-ECB加密
     * @param keyHex 16字节密钥的十六进制字符串
     * @param plaintext 明文字符串
     * @return 加密后的十六进制字符串
     */
    public String ecbEncrypt(String keyHex, String plaintext) {
        SM4JNI sm4 = new SM4JNI();
        byte[] key = keyHex.getBytes();
        byte[] plainBytes = plaintext.getBytes(StandardCharsets.UTF_8);

        byte[] ciphertext = new byte[plainBytes.length + 32]; // 预留padding空间
        int cipherLen = sm4.ecbEncrypt(key, plainBytes, ciphertext);

        if (cipherLen <= 0) {
            throw new RuntimeException("SM4 ECB加密失败");
        }

        return bytesToHex(Arrays.copyOf(ciphertext, cipherLen));
    }

    /**
     * SM4-ECB解密
     * @param keyHex 16字节密钥的十六进制字符串
     * @param ciphertextHex 密文的十六进制字符串
     * @return 解密后的原始字符串
     */
    public String ecbDecrypt(String keyHex, String ciphertextHex) {
        SM4JNI sm4 = new SM4JNI();
        byte[] key = keyHex.getBytes();
        byte[] ciphertext = hexToBytes(ciphertextHex);

        byte[] plaintext = new byte[ciphertext.length];
        int plainLen = sm4.ecbDecrypt(key, ciphertext, plaintext);

        if (plainLen <= 0) {
            throw new RuntimeException("SM4 ECB解密失败");
        }

        return new String(plaintext, 0, plainLen, StandardCharsets.UTF_8);
    }

    /**
     * 字节数组转十六进制字符串
     */
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }

    /**
     * 十六进制字符串转字节数组
     */
    public static byte[] hexToBytes(String hex) {
        int len = hex.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4)
                    + Character.digit(hex.charAt(i+1), 16));
        }
        return data;
    }

//    public static void main(String[] args) {
//        SM4JNI sm4 = new SM4JNI();
//
//        // ׼������
//        byte[] key = new byte[16]; // 16�ֽ�ȫ0��Կ
//        byte[] iv = "iv1234567890abc!".getBytes(); // 16�ֽ� IV
//        byte[] plain = "Hello SM4! Hello SM4 !Hello SM4! Hello SM4! Hello SM4 !Hello SM4!".getBytes(); // ����
//
//        System.out.println("Message: " + new String(plain));
//
//        // --- CBC ���� ---
//        byte[] cipherCbc = new byte[plain.length + 32]; // Ԥ�� padding �ռ�
//        int cbcEncLen = sm4.cbcEncrypt(key, iv, plain, cipherCbc);
//        if (cbcEncLen <= 0) {
//            System.err.println("SM4 CBC encryption failed.");
//            return;
//        }
//        System.out.println("SM4 CBC ciphertext length: " + cbcEncLen);
//        System.out.print("SM4 CBC ciphertext: ");
//        for (int i = 0; i < cbcEncLen; i++) {
//            System.out.printf("%02X", cipherCbc[i]);
//        }
//        System.out.println();
//
//        // --- CBC ���� ---
//        byte[] cipherCbcActual = Arrays.copyOf(cipherCbc, cbcEncLen); // ��ȡʵ�����ĳ���
//        byte[] plainCbc = new byte[cipherCbcActual.length]; // �������������
//        int cbcDecLen = sm4.cbcDecrypt(key, iv, cipherCbcActual, plainCbc);
//        if (cbcDecLen <= 0) {
//            System.err.println("SM4 CBC decryption failed.");
//            return;
//        }
//        System.out.println("SM4 CBC plaintext length: " + cbcDecLen);
//        System.out.println("SM4 CBC plaintext: " + new String(plainCbc, 0, cbcDecLen));
//
//        // --- ECB ���� ---
//        byte[] cipherEcb = new byte[plain.length + 32];
//        int ecbEncLen = sm4.ecbEncrypt(key, plain, cipherEcb);
//        if (ecbEncLen <= 0) {
//            System.err.println("SM4 ECB encryption failed.");
//            return;
//        }
//        System.out.println("SM4 ECB ciphertext length: " + ecbEncLen);
//        System.out.print("SM4 ECB ciphertext: ");
//        for (int i = 0; i < ecbEncLen; i++) {
//            System.out.printf("%02X", cipherEcb[i]);
//        }
//        System.out.println();
//
//        // --- ECB ���� ---
//        byte[] cipherEcbActual = Arrays.copyOf(cipherEcb, ecbEncLen); // �ü�����
//        byte[] plainEcb = new byte[cipherEcbActual.length];
//        int ecbDecLen = sm4.ecbDecrypt(key, cipherEcbActual, plainEcb);
//        if (ecbDecLen <= 0) {
//            System.err.println("SM4 ECB decryption failed.");
//            return;
//        }
//        System.out.println("EBC plaintext length: " + cbcDecLen);
//        System.out.println("EBC plaintext: " + new String(plainCbc, 0, ecbDecLen));
//    }
}